-- FiveM YumV plugin by Akkariin
-- This plugin is an open-source project
-- Use GPL v3 License
-- https://github.com/kasuganosoras/yumv

local yum = {}

-- FiveM YumV config
yum.mirror = "https://yumv.net/"
yum.version = "1.0.3"
yum.versionnum = 103
-- End

local downloading = 0
local current_dir = string.gsub(GetResourcePath(GetCurrentResourceName()), "//", "/")

-- 解压文件
function unzip(s, d)
	os.execute("unzip -q -o '" .. s .. "' -d '" .. d .. "'")
end

-- 网络请求
function curl(url, method, data, headers, timeout)
	
	if url == nil then
		return 'url undefined'
	end
	
	if method == nil then
		method = 'GET'
	end
	
	if data == nil then
		data = ''
	end
	
	if headers == nil then
		headers = {}
	end
	
	if timeout == nil then
		timeout = 5000
	end
	
	local curl_result = {}
	
	if string.lower(method) == 'post' then
		local postString = ''
		for i, v in pairs(data) do
			postString = postString .. i .. "=" .. urlencode(v) .. "&"
		end
		postString = string.sub(postString, 1, string.len(postString) - 1)
		headers["Content-Type"] = 'application/x-www-form-urlencoded'
		PerformHttpRequest(url, function (errorCode, resultData, resultHeaders)
			curl_result.status = errorCode
			curl_result.body = resultData
			curl_result.header = resultHeaders
		end, method, postString, headers)
	else
		PerformHttpRequest(url, function (errorCode, resultData, resultHeaders)
			curl_result.status = errorCode
			curl_result.body = resultData
			curl_result.header = resultHeaders
		end, method, data, headers)
	end
	whilewait = 0
	while curl_result.status == nil do
		Wait(1)
		if whilewait > timeout then
			break
		else
			whilewait = whilewait + 1
		end
	end
	return curl_result
end

-- URL 编码器
function urlencode(str)
   if (str) then
      str = string.gsub (str, "\n", "\r\n")
      str = string.gsub (str, "([^%w ])",
         function (c) return string.format ("%%%02X", string.byte(c)) end)
      str = string.gsub (str, " ", "+")
   end
   return str    
end

-- URL 解码器
function urldecode(s)
	s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end)
	return s
end

-- 读取文件
function file_get_contents(file)
    local f = assert(io.open(file, "rb"))
    local content = f:read("*all")
    f:close()
    return content
end

-- 执行命令
function shell_exec(command)
	os.execute(command .. " > /tmp/fivem-yum-shell-output.log")
	result = file_get_contents('/tmp/fivem-yum-shell-output.log')
	os.remove('/tmp/fivem-yum-shell-output.log')
	return result
end

-- 实时输出的 shell-output
function realtimeShell(cmd)
	f = assert(io.popen(cmd))
	for line in f:lines() do
		print(line)
	end
	f:close()
end

-- 检测文件是否存在
function file_exists(name)
    if type(name)~="string" then return false end
    return os.rename(name,name) and true or false
end

-- 写入文件
function file_put_contents(name, data, append)
	if append == true then
		writemode = "a"
	else
		writemode = "w"
	end
	file = io.open(name, writemode)
	file:write(data)
	file:close()
end

-- 遍历 table
function forEach(tb)
	for key, value in pairs(tb) do      
		print(key .. " => " .. value)  
	end
end

-- 取字符串长度
function strlen(str)
	local _,n = str:gsub('[\128-\255]', '')
	return #str - n / 2
end

-- 列出所有插件
function listAllPlugin(tb)
	print("+----------------------------------+------------------+")
	print("| Name                             | Version          |")
	print("+----------------------------------+------------------+")
	local i = 0
	for key, value in pairs(tb) do
		i = i + 1
		local padding = " "
		local padding2 = " "
		local length = strlen(value.name)
		local length2 = strlen(tostring(value.version))
		if length <= 32 then
			padding = string.rep(" ", 32 - length)
		end
		if length2 <= 16 then
			padding2 = string.rep(" ", 16 - length2)
		end
		print("| " .. value.name .. padding .. " | " .. tostring(value.version) .. padding2 .. " |")
	end
	local length3 = strlen(tostring(i))
	padding3 = string.rep(" ", 51 - length3 - 19)
	print("+----------------------------------+------------------+")
	print("| Total: " .. tostring(i) .. " resource(s)" .. padding3 .. " |")
	print("+----------------------------------+------------------+")
end

-- 加载所有插件
function loadPluginList()
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			print("[YumV] No plugin installed")
		else
			for key, value in pairs(data.list) do
				print("[YumV] Loading plugin: " .. value.name)
				StartResource(value.name)
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
end

-- 检查插件是否已安装（包括非 yum 安装的插件）
function checkPluginExist(name)
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			print("[YumV] No plugin installed")
		else
			for key, value in pairs(data.list) do
				if value.name == name then
					return true
				end
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
	local resource_status = GetResourceState(name)
	if resource_status == "missing" or resource_status == "stopped" then
		return false
	else
		return true
	end
end

-- 检查插件是否已安装（仅限 yum 安装的插件）
function checkPluginInList(name)
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			print("[YumV] No plugin installed")
		else
			for key, value in pairs(data.list) do
				if value.name == name then
					return true
				end
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
	return false
end

-- 获取插件版本号
function getPluginVersion(name)
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			return false
		else
			for key, value in pairs(data.list) do
				if value.name == name then
					return value.version
				end
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
	return false
end

-- 获取插件版本号
function getPluginPath(name)
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			return false
		else
			for key, value in pairs(data.list) do
				if value.name == name then
					if value.path == nil then
						value.path = "/[YumV-plugins]/"
					end
					return value.path
				end
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
	return false
end

-- 从列表中删除一个插件
function removePluginFromList(name)
	if file_exists(current_dir .. "/list.json") then
		local data = file_get_contents(current_dir .. "/list.json")
		data = json.decode(data)
		if data.list == nil then
			print("[YumV] No plugin installed")
		else
			for key, value in pairs(data.list) do
				if value.name == name then
					table.remove(data.list, key)
					file_put_contents(current_dir .. "/list.json", json.encode(data))
					return true
				end
			end
		end
	else
		print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
	end
	return false
end

-- 增加一个新插件到列表
function addNewPlugin(name, version, path)
	local data = file_get_contents(current_dir .. "/list.json")
	data = json.decode(data)
	if data.list == nil then
		data.list = {}
	end
	table.insert(data.list, {name = name, version = version, path = path})
	file_put_contents(current_dir .. "/list.json", json.encode(data))
end

-- 检查 YumV 更新
function checkUpdate()
	print("[YumV] Checking for YumV new version...")
	Citizen.CreateThread(function()
		local url = yum.mirror .. "?s=check_update&yum_version=" .. urlencode(yum.versionnum)
		local rs = curl(url, 'GET')
		if rs.status == 200 then
			print(rs.body)
		else
			print("[YumV] Failed check plugin update, please check your network.")
		end
	end)
end

function updateSelf()
	print("[YumV] Updating YumV to new version...")
	Citizen.CreateThread(function()
		local url = yum.mirror .. "?s=get_update&yum_version=" .. urlencode(yum.versionnum)
		local rs = curl(url, 'GET')
		if rs.status == 200 then
			local new_version = tonumber(rs.body)
			if yum.versionnum == new_version then
				print("[YumV] Your YumV is newest version, not need to update.")
			else
				url = yum.mirror .. "?s=get_update_download&yum_version=" .. urlencode(rs.body)
				rs = curl(url, 'GET')
				if rs.status == 200 then
					print("[YumV] Downloading files, please don't stop the server.")
					local cmd = "wget -q -4 \"" .. rs.body .. "\" -O \"" .. current_dir .. "/update.zip\""
					-- print(cmd)
					file_put_contents(current_dir .. "/update.zip", "")
					-- 开始下载
					os.execute(cmd)
					print("[YumV] Download finished, decompressing... ")
					unzip(current_dir .. "/update.zip", current_dir .. "/")
					os.remove(current_dir .. "/update.zip");
					print("[YumV] Update finished, please execute command: \"restart yum\".")
				else
					print("[YumV] Failed check plugin update, server return: " .. tostring(rs.status))
				end
			end
		else
			print("[YumV] Failed check plugin update, server return: " .. tostring(rs.status))
		end
	end)
end

-- 安装插件
function installPlugin(name, version)
	local url = ""
	if version == nil then
		url = yum.mirror .. "?s=download&name=" .. urlencode(name) .. "&yum_version=" .. urlencode(yum.versionnum)
	else
		url = yum.mirror .. "?s=download&name=" .. urlencode(name) .. "&version=" .. urlencode(version) .. "&yum_version=" .. urlencode(yum.versionnum)
	end
	local rs = curl(url, 'GET')
	if rs.status == 200 then
		local plugin_name = rs.header.plugin
		local plugin_version = rs.header.version
		local plugin_size = rs.header.size
		local plugin_rsize = rs.header.realsize
		local plugin_dir = rs.header.installdir
		downloading = plugin_rsize
		if (rs.header.depend == nil) == false then
			depend = json.decode(rs.header.depend)
			for key, value in pairs(depend) do
				if checkPluginExist(value.name) == false then
					print("[YumV] Plugin " .. name .. " has depend " .. value.name .. " need to install")
					local result = installPlugin(value.name, value.version)
					if result == false then
						print("[YumV] Depend " .. value.name .. " install failed!")
					end
				end
			end
		end
		if plugin_name == nil or plugin_version == nil then
			print("[YumV] An error occurred when fetch the command name")
			return false
		else
			if plugin_dir == nil or plugin_dir == "" then
				plugin_dir = "/[YumV-plugins]/"
			end
			if checkPluginExist(plugin_name) == false then
				print("[YumV] Found plugin: " .. plugin_name)
				if not file_exists(current_dir .. "/.." .. plugin_dir .. plugin_name .. "/") then
					os.execute("mkdir -p '" .. current_dir .. "/.." .. plugin_dir .. plugin_name .. "/'")
				end
				print("[YumV] Downloading: " .. rs.body .. " => /tmp/fivem-yum-temp.zip (" .. plugin_size .. ")")
				local cmd = "wget -q -4 \"" .. rs.body .. "\" -O \"/tmp/fivem-yum-temp.zip\" &"
				-- print(cmd)
				file_put_contents("/tmp/fivem-yum-temp.zip", "")
				-- 开始下载
				os.execute(cmd)
				local finished = false
				local last = 0
				local speed = 0
				local dspeed = ""
				while finished == false do
					local f = assert(io.open("/tmp/fivem-yum-temp.zip", "rb"))
					local size = f:seek("end")
					f:close()
					if size >= tonumber(plugin_rsize) then
						finished = true
					end
					speed = size - last
					last = size
					if speed < 1048576 then
						dspeed = string.format("%.2f", speed / 1024) .. "KB/s"
					else
						dspeed = string.format("%.2f", speed / 1048576) .. "MB/s"
					end
					finish = tostring((size / tonumber(plugin_rsize)) * 100)
					finish = string.format("%.2f", finish)
					io.write("\r[YumV] Download status: " .. tostring(finish) .. "% - " .. dspeed .. "           ")
					Wait(1000)
				end
				print("")
				Wait(1000)
				print("[YumV] Download finished, decompressing... ")
				unzip('/tmp/fivem-yum-temp.zip', current_dir .. "/.." .. plugin_dir .. rs.header.plugin .. "/")
				ExecuteCommand("refresh")
				io.write("[YumV] ")
				if StartResource(plugin_name) then
					addNewPlugin(plugin_name, tonumber(plugin_version), plugin_dir)
					print("[YumV] Plugin " .. plugin_name .. " install successful!")
					return true
				else
					print("[YumV] Failed to start resource! please check your console output to see more info.")
					return false
				end
			else
				print("[YumV] Plugin already installed! please use \"yum update " .. plugin_name .. "\" to update it, or use \"yum remove " .. plugin_name .. "\" to remove it!")
				print("[YumV] Current resource status: " .. GetResourceState(plugin_name))
				return true
			end
		end
	else
		print("[YumV] Cannot install '" .. name .. "', server return error: " .. rs.status)
		return false
	end
end

-- 输出帮助信息
function printHelp()
	print("FiveM YumV plugin by Akkariin")
	print("Usage: yum <command> [args]")
	print("")
	print("Commands:")
	print("    install <plugin> [version]    Install a new plugin to server")
	print("    remove <plugin>               Delete the plugin from server")
	print("    update <plugin>               Update a plugin to new version")
	print("    check <plugin>                Check whether a plugin has a new version")
	print("    search <name>                 Search a plugin in mirror database")
	print("    list                          List all installed plugin")
	print("    version                       Show the yum plugin version")
	print("    checkyumv                     Check if YumV has new version")
	print("    updateself                    Update YumV to newest version")
end

-- 注册命令
RegisterCommand("yum", function(source, args, rawCommand)
	-- 默认的帮助
    if args[1] == nil or args[1] == "help" then
		printHelp()
	else
		if args[1] == "test" then
			ExecuteCommand("restart yum")
			
		-- 删除一个插件
		elseif args[1] == "remove" then
			if args[2] == nil then
				print("[YumV] Please provide the plugin name you want to remove: yum remove <name>")
			else
				if checkPluginInList(args[2]) then
					StopResource(args[2])
					local rmdir = getPluginPath(args[2])
					os.execute("rm -rf '" .. current_dir .. "/.." .. rmdir .. args[2] .. "/'")
					if removePluginFromList(args[2]) then
						print("[YumV] Plugin delete successful!")
					else
						print("[YumV] Failed to delete this plugin!")
					end
				else
					print("[YumV] Cannot found this plugin, maybe it is not install by YumV?")
				end
			end
			
		-- 修复列表错误
		elseif args[1] == "fix-list" then
			if file_exists(current_dir .. "/list.json") then
				print("[YumV] The resource list already exist, not need to fix!")
			else
				file_put_contents(current_dir .. "/list.json", json.encode(
					{
						version = "1.0", list = {}
					}
				))
				print("[YumV] Successful fix the plugin list")
			end
			
		-- 列出已安装的插件
		elseif args[1] == "list" then
			if file_exists(current_dir .. "/list.json") then
				local data = file_get_contents(current_dir .. "/list.json")
				data = json.decode(data)
				if data.list == nil then
					print("[YumV] No plugin installed")
				else
					listAllPlugin(data.list)
				end
			else
				print("[YumV] Cannot found resources list! Please use \"yum fix-list\" to fix it!")
			end
			
		-- 搜索一个插件
		elseif args[1] == "search" then
			if args[2] == nil then
				print("[YumV] Please provide the plugin name you want to search: yum search <name>")
			else
				print("Loading data from mirror server...")
				local url = yum.mirror .. "?s=search&name=" .. urlencode(args[2])
				local rs = curl(url, 'GET')
				print("")
				print(rs.body)
			end
			
		-- 安装一个插件
		elseif args[1] == "install" then
			if args[2] == nil then
				print("[YumV] Please provide the plugin name you want to install: yum install <name>")
			else
				CreateThread(function()
					print("")
					if args[3] == nil then
						local result = installPlugin(args[2])
					else
						local result = installPlugin(args[2], args[3])
					end
					if result == false then
						print("[YumV] One or more plugin(s) install failed, please check your console output for more info.")
					else
						print("[YumV] All plugin(s) install successful.")
					end
				end)
			end
			
		-- 更新一个插件
		elseif args[1] == "update" then
			if args[2] == nil then
				print("[YumV] Please provide the plugin name you want to install: yum install <name>")
			else
				if checkPluginInList(args[2]) then
					local updateurl = yum.mirror .. "?s=download&name=" .. urlencode(args[2])
					local rs = curl(updateurl, 'GET')
					print("")
					if rs.status == 200 then
						local plugin_name = rs.header.plugin
						local plugin_version = tonumber(rs.header.version)
						local local_version = tonumber(getPluginVersion(plugin_name))
						local plugin_dir = rs.header.installdir
						if local_version == false then
							print("[YumV] Cannot update '" .. args[2] .. "', failed to get local version, please check your list.json!")
						else
							if plugin_version > local_version then
								if plugin_dir == nil or plugin_dir == "" then
									plugin_dir = "/[YumV-plugins]/"
								end
								if not file_exists(current_dir .. "/.." .. plugin_dir .. plugin_name .. "/") then
									os.execute("mkdir -p '" .. current_dir .. "/.." .. plugin_dir .. plugin_name .. "/'")
								end
								print("[YumV] Downloading: " .. rs.body)
								local cmd = "wget -q -4 \"" .. rs.body .. "\" -O \"/tmp/fivem-yum-temp.zip\""
								-- print(cmd)
								os.execute(cmd)
								print("[YumV] Download finished, deleting old version... ")
								StopResource(args[2])
								os.execute("rm -rf '" .. current_dir .. "/.." .. plugin_dir .. args[2] .. "/'")
								removePluginFromList(plugin_name)
								print("[YumV] Decompressing file... ")
								unzip('/tmp/fivem-yum-temp.zip', current_dir .. "/.." .. plugin_dir .. rs.header.plugin .. "/")
								ExecuteCommand("refresh")
								if StartResource(plugin_name) then
									addNewPlugin(plugin_name, plugin_version, plugin_dir)
									print("[YumV] Plugin update successful!")
								else
									print("[YumV] Failed to start resource! please check your console output to see more info.")
								end
							else
								print("[YumV] Your plugin is already up to date!")
							end
						end
					else
						print("[YumV] Cannot update '" .. args[2] .. "', server return error: " .. rs.status)
					end
				else
					print("[YumV] Cannot update '" .. args[2] .. "', plugin not installed.")
				end
			end
			
		-- 检查插件是否有更新
		elseif args[1] == "check" then
			if args[2] == nil then
				print("[YumV] Please provide the plugin name you want to check: yum check <name>")
			else
				if checkPluginInList(args[2]) then
					local updateurl = yum.mirror .. "?s=download&name=" .. urlencode(args[2])
					local rs = curl(updateurl, 'GET')
					print("")
					if rs.status == 200 then
						local plugin_name = rs.header.plugin
						local plugin_version = tonumber(rs.header.version)
						local local_version = tonumber(getPluginVersion(plugin_name))
						if local_version == false then
							print("[YumV] Cannot update '" .. args[2] .. "', failed to get local version, please check your list.json!")
						else
							if plugin_version > local_version then
								print("[YumV] This plugin has a new version: " .. tostring(plugin_version) .. ". The version you have installed is: " .. tostring(local_version))
							else
								print("[YumV] Your plugin is already up to date!")
							end
						end
					else
						print("[YumV] Cannot check update for '" .. args[2] .. "', server return error: " .. rs.status)
					end
				else
					print("[YumV] Cannot check update for '" .. args[2] .. "', plugin not installed.")
				end
			end
			
		-- 获取 yum 版本号
		elseif args[1] == "version" then
			print("FiveM YumV plugin by Akkariin")
			print("Version: " .. yum.version)
		
		-- 检查 YumV 新版本
		elseif args[1] == "checkyumv" then
			checkUpdate()
		
		-- 自动更新到新版本
		elseif args[1] == "updateself" then
			updateSelf()
			
		-- 默认输出
		else
			print("Command not found: " .. args[1] .. ", use \"yum help\" to get help.")
		end
	end
end, true)

-- Load all installed plugins
loadPluginList()
checkUpdate()
